import { MessageCenter, defer } from "utils-lib-js"

/**
 * 队列参数
 * queue: 队列列表
 * id: 队列唯一标识
 * result: 运行完成后的结果
 * 
 */
export type IQueues<F = Function[]> = {
    queue: F
    id?: string
    result?: unknown[]
}
// 系统内部队列
export type IQueueList = IQueues<Function>[]
// 参数传入的队列结构
export type IQueuesParams = Function[]
/**
 * 队列缓存结构
 */
export type IQueueTemp = {
    [key: string]: IQueues
}

/**
 * 队列状态 idle:空闲 pending:等待 fulfilled:完成 rejected:失败
 */
export type IState = "idle" | "pending" | "fulfilled" | "rejected"
export type IStepCbParams = {
    key: string
    step: number
    result: unknown[]
    queues: IQueuesParams
}
/**
 * 任务队列参数
 */
export type ITaskQueueProps = {
    stepCb?: (IStepCbParams) => void// 根据maxLen分割的队列会执行的回调，每一个key对应一个队列，step表示当前key队列的第几步
    maxLen?: number
}
export class TaskQueue extends MessageCenter {
    props: ITaskQueueProps
    queues: IQueueList
    queueTemp: IQueueTemp
    state: IState
    defaultKey: string = "default"
    defaultLen: number = 10

    constructor(props: ITaskQueueProps) {
        super()
        this.clearQueue()
        this.props = props
    }
    /**
     * 进入队列
     * @param queue: IQueues 单个队列
     * @returns promise: Promise<void> 当前队列执行结束的异步操作
     */
    push = (queue: IQueuesParams, id: string = this.defaultKey) => {
        this.checkQueue(queue, id)
        const { resolve, reject, promise } = defer()
        try {
            this.formatQueue(queue, id)
            this.run()
        } catch (e) {
            this.state = "rejected"
            reject(e)
        }
        this.on(id, resolve)
        return promise
    }
    /**
    * 异步执行队列:函数的思路是循环,通过当前队列状态和数量判断是否执行
    * @returns void 0
    */
    run = async () => {
        while (!!!(this.state === 'pending' || (this.queues.length === 0 && (this.state = "idle")))) {
            this.state = "pending"
            const queues = this.unshift(this.props?.maxLen ?? this.defaultLen)
            const result = await Promise.all(queues.map((item) => {
                const res = item.queue()
                if (this.isPromise(res)) {
                    return res?.catch?.(e => new Error(e))
                }
                return res
            }))
            this.state = 'fulfilled'
            this.finish({ result, queues })
        }
    }
    /**
    * 处理每次队列执行完成后的数据
    * @param data { result, queues } 运行结束后的返回值及削峰后的初始队列，一一对应
    */
    private finish = ({ result = [], queues = [] }) => {
        const { queueTemp } = this
        queues.forEach((it: IQueues<Function>, i) => {
            const key = it.id
            const temp = queueTemp[key]
            temp.result.push(result[i])
            if (!!!i) {// 0项
                const step = this.getStep(key)
                const data = { key, step, result, queues }
                this.props?.stepCb?.(data)
                this.emit(`${key}:${step}`, data)
            }
            if (temp.result?.length === temp.queue?.length) {
                this.emit(key, temp.result)
                this.state = 'idle'
                queueTemp[key] = null
            }
        });
    }
    /**
     * 格式化队列
     * @param fns 函数列表
     * @param id id标识
     */
    private formatQueue = (fns: IQueuesParams, id: string) => {
        const { queueTemp } = this
        const __obj = { id, result: [] }
        const it = queueTemp[id] ?? { queue: [], ...__obj }
        const tempQueue = it?.queue
        fns.forEach(queue => {
            tempQueue.push(queue)
            this.queues.push({ queue, ...__obj })
        })
        queueTemp[id] = it
    }
    isPromise = (fn: any) => {
        return fn instanceof Promise
    }
    voidObj = () => Object.create(null)
    /**
     * 移出队列
     * @param length 移出数量
     * @returns queues 移出的队列
     */
    unshift = (length: number = this.defaultLen) => {
        return this.queues.splice(0, length)
    }
    getStep = (key: string) => {
        const { queues, queueTemp } = this
        return Math.ceil((queueTemp[key].queue.length - queues.length) / (this.props?.maxLen ?? 1))
    }

    /**
     * 初始化整个队列，清除所有数据
     */
    clearQueue = () => {
        this.queues = []
        this.queueTemp = this.voidObj()
        this.props = null
        this.state = "idle"
        this.clear()
    }
    /**
     * 检查参数是否符合标准
     * @param queue 队列或队列集合
     */
    private checkQueue(queue: IQueuesParams, id: string) {
        if (!queue) {
            throw new ReferenceError('queue is not defined')
        }
        if (!(queue instanceof Array) || typeof queue !== "object") {
            throw new TypeError(`queue should be an object and queue should be an array`);
        }
        const noFn = i => !i || typeof i !== "function"
        if (queue?.length === 0) throw new Error('queue.length can not be 0')
        if (queue?.find((i) => noFn(i))) throw new Error('queueList should have fn')
        // if (id && id !== this.defaultKey && this.queueTemp[id]) throw new Error(`${id} is already defined`)
    }
}

export default TaskQueue;